/*******************************************************************************
 * Copyright (c) 2004, 2009 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.aseclipse.osgi.classes {

import org.aseclipse.osgi.BundleContext;
import org.aseclipse.osgi.BundleFramework;
import org.aseclipse.osgi.ServiceEvent;
import org.aseclipse.osgi.ServiceRegistration;

import flash.events.EventDispatcher;
import flash.utils.Dictionary;
import flash.utils.getDefinitionByName;

import mx.collections.ArrayCollection;

/**
 * The Service Registry. This class is the main control point for service 
 * layer operations in the framework.
 * 
 * @ThreadSafe
 */
public class ServiceRegistry extends EventDispatcher {
	public static const SERVICEEVENT:int = 3;

	/** Published services by class name. 
	 * Map&lt;String,List&lt;ServiceRegistrationImpl&gt;&gt;
	 * The List&lt;ServiceRegistrationImpl&gt;s are sorted.
	 */
	/* @GuardedBy("this") */
	private var /*Map<String,List<ServiceRegistrationImpl>>*/publishedServicesByClass:Dictionary;
	/** All published services. 
	 * List&lt;ServiceRegistrationImpl&gt;.
	 * The List&lt;ServiceRegistrationImpl&gt;s are sorted.
	 */
	/* @GuardedBy("this") */
	private var /*List<ServiceRegistrationImpl>*/allPublishedServices:Array;
	/** Published services by BundleContext.  
	 * Map&lt;BundleContext,List&lt;ServiceRegistrationImpl&gt;&gt;.
	 * The List&lt;ServiceRegistrationImpl&gt;s are NOT sorted.
	 */
	/* @GuardedBy("this") */
	private var /*Map<BundleContext,List<ServiceRegistrationImpl>>*/publishedServicesByContext:Dictionary;
	/** next free service id. */
	/* @GuardedBy("this") */
	private var serviceid:Number;
	/** Active Service Listeners.
	 * Map&lt;BundleContext,CopyOnWriteIdentityMap&lt;ServiceListener,FilteredServiceListener&gt;&gt;.
	 */
	/* @GuardedBy("serviceEventListeners") */
	private var /*Map<BundleContext,CopyOnWriteIdentityMap<ServiceListener,FilteredServiceListener>>*/serviceEventListeners:Dictionary;

	/** initial capacity of the main data structure */
	private static var initialCapacity:int = 50;
	/** initial capacity of the nested data structure */
	private static var initialSubCapacity:int = 10;
	/** framework which created this service registry */
	private var framework:BundleFramework;

	/**
	 * Initializes the internal data structures of this ServiceRegistry.
	 *
	 */
	public function ServiceRegistry(framework:BundleFramework) {
		this.framework = framework;
		serviceid = 1;
		publishedServicesByClass = new Dictionary(); // initialCapacity
		publishedServicesByContext = new Dictionary(); // initialCapacity
		allPublishedServices = new Array(); // initialCapacity
		serviceEventListeners = new Dictionary();  // initialCapacity
	}

	/**
	 * Registers the specified service object with the specified properties
	 * under the specified class names into the Framework. A
	 * <code>ServiceRegistrationImpl</code> object is returned. The
	 * <code>ServiceRegistrationImpl</code> object is for the private use of the
	 * bundle registering the service and should not be shared with other
	 * bundles. The registering bundle is defined to be the context bundle.
	 * Other bundles can locate the service by using either the
	 * {@link #getServiceReferences} or {@link #getServiceReference} method.
	 * 
	 * <p>
	 * A bundle can register a service object that implements the
	 * {@link ServiceFactory} interface to have more flexibility in providing
	 * service objects to other bundles.
	 * 
	 * <p>
	 * The following steps are required to register a service:
	 * <ol>
	 * <li>If <code>service</code> is not a <code>ServiceFactory</code>,
	 * an <code>IllegalArgumentException</code> is thrown if
	 * <code>service</code> is not an <code>instanceof</code> all the
	 * classes named.
	 * <li>The Framework adds these service properties to the specified
	 * <code>Dictionary</code> (which may be <code>null</code>): a property
	 * named {@link Constants#SERVICE_ID} identifying the registration number of
	 * the service and a property named {@link Constants#OBJECTCLASS} containing
	 * all the specified classes. If any of these properties have already been
	 * specified by the registering bundle, their values will be overwritten by
	 * the Framework.
	 * <li>The service is added to the Framework service registry and may now
	 * be used by other bundles.
	 * <li>A service event of type {@link ServiceEvent#REGISTERED} is fired.
	 * <li>A <code>ServiceRegistration</code> object for this registration is
	 * returned.
	 * </ol>
	 * 
	 * @param context The BundleContext of the registering bundle.
	 * @param clazzes The class names under which the service can be located.
	 *        The class names in this array will be stored in the service's
	 *        properties under the key {@link Constants#OBJECTCLASS}.
	 * @param service The service object or a <code>ServiceFactory</code>
	 *        object.
	 * @param properties The properties for this service. The keys in the
	 *        properties object must all be <code>String</code> objects. See
	 *        {@link Constants} for a list of standard service property keys.
	 *        Changes should not be made to this object after calling this
	 *        method. To update the service's properties the
	 *        {@link ServiceRegistration#setProperties} method must be called.
	 *        The set of properties may be <code>null</code> if the service
	 *        has no properties.
	 * 
	 * @return A <code>ServiceRegistrationImpl</code> object for use by the bundle
	 *         registering the service to update the service's properties or to
	 *         unregister the service.
	 * 
	 * @throws java.lang.IllegalArgumentException If one of the following is
	 *         true:
	 *         <ul>
	 *         <li><code>service</code> is <code>null</code>.
	 *         <li><code>service</code> is not a <code>ServiceFactory</code>
	 *         object and is not an instance of all the named classes in
	 *         <code>clazzes</code>.
	 *         <li><code>properties</code> contains case variants of the same
	 *         key name.
	 *         </ul>
	 * 
	 * @throws java.lang.SecurityException If the caller does not have the
	 *         <code>ServicePermission</code> to register the service for all
	 *         the named classes and the Java Runtime Environment supports
	 *         permissions.
	 * 
	 * @throws java.lang.IllegalStateException If this BundleContext is no
	 *         longer valid.
	 * 
	 * @see ServiceRegistration
	 * @see ServiceFactory
	 */
	public function registerService(context:BundleContext, clazzes:Array, service:Object, properties:Dictionary):ServiceRegistrationImpl {
		if (service == null) {
			throw new Error("Service object is null");
		}

		var size:int = clazzes.length;

		if (size == 0) {
			throw new Error("Classes array is empty");
		}

		/* copy the array so that changes to the original will not affect us. */
		//var copy:Array = new Array;
		// intern the strings and remove duplicates
/* 		for (var i:int = 0; i < size; i++) {
			String clazz = clazzes[i].intern();
			if (!copy.contains(clazz)) {
				copy.add(clazz);
			}
		} */
		//size = copy.size();
		//clazzes = (String[]) copy.toArray(new String[size]);

		/* check for ServicePermissions. */
		//checkRegisterServicePermission(clazzes);

		var registration:ServiceRegistrationImpl = new ServiceRegistrationImpl(this, context, clazzes, service);
		registration.register(properties);
		return registration;
	}

	/**
	 * Returns an array of <code>ServiceReferenceImpl</code> objects. The returned
	 * array of <code>ServiceReferenceImpl</code> objects contains services that
	 * were registered under the specified class, match the specified filter
	 * criteria, and the packages for the class names under which the services
	 * were registered match the context bundle's packages as defined in
	 * {@link ServiceReference#isAssignableTo(Bundle, String)}.
	 * 
	 * <p>
	 * The list is valid at the time of the call to this method, however since
	 * the Framework is a very dynamic environment, services can be modified or
	 * unregistered at anytime.
	 * 
	 * <p>
	 * <code>filter</code> is used to select the registered service whose
	 * properties objects contain keys and values which satisfy the filter. See
	 * {@link Filter} for a description of the filter string syntax.
	 * 
	 * <p>
	 * If <code>filter</code> is <code>null</code>, all registered services
	 * are considered to match the filter. If <code>filter</code> cannot be
	 * parsed, an {@link InvalidSyntaxException} will be thrown with a human
	 * readable message where the filter became unparsable.
	 * 
	 * <p>
	 * The following steps are required to select a set of
	 * <code>ServiceReferenceImpl</code> objects:
	 * <ol>
	 * <li>If the filter string is not <code>null</code>, the filter string
	 * is parsed and the set <code>ServiceReferenceImpl</code> objects of
	 * registered services that satisfy the filter is produced. If the filter
	 * string is <code>null</code>, then all registered services are
	 * considered to satisfy the filter.
	 * <li>If the Java Runtime Environment supports permissions, the set of
	 * <code>ServiceReferenceImpl</code> objects produced by the previous step is
	 * reduced by checking that the caller has the
	 * <code>ServicePermission</code> to get at least one of the class names
	 * under which the service was registered. If the caller does not have the
	 * correct permission for a particular <code>ServiceReferenceImpl</code>
	 * object, then it is removed from the set.
	 * <li>If <code>clazz</code> is not <code>null</code>, the set is
	 * further reduced to those services that are an <code>instanceof</code>
	 * and were registered under the specified class. The complete list of
	 * classes of which a service is an instance and which were specified when
	 * the service was registered is available from the service's
	 * {@link Constants#OBJECTCLASS} property.
	 * <li>The set is reduced one final time by cycling through each
	 * <code>ServiceReference</code> object and calling
	 * {@link ServiceReference#isAssignableTo(Bundle, String)} with the context
	 * bundle and each class name under which the <code>ServiceReference</code>
	 * object was registered. For any given <code>ServiceReferenceImpl</code>
	 * object, if any call to
	 * {@link ServiceReference#isAssignableTo(Bundle, String)} returns
	 * <code>false</code>, then it is removed from the set of
	 * <code>ServiceReferenceImpl</code> objects.
	 * <li>An array of the remaining <code>ServiceReferenceImpl</code> objects is
	 * returned.
	 * </ol>
	 * 
	 * @param context The BundleContext of the requesting bundle.
	 * @param clazz The class name with which the service was registered or
	 *        <code>null</code> for all services.
	 * @param filterstring The filter criteria.
	 * @param allservices True if the bundle called getAllServiceReferences.
	 * @return An array of <code>ServiceReferenceImpl</code> objects or
	 *         <code>null</code> if no services are registered which satisfy
	 *         the search.
	 * @throws InvalidSyntaxException If <code>filter</code> contains an
	 *         invalid filter string that cannot be parsed.
	 * @throws java.lang.IllegalStateException If this BundleContext is no
	 *         longer valid.
	 */
	public function getServiceReferences(context:BundleContext, clazz:String, filterstring:String, allservices:Boolean):Array {
		//if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
		//	Debug.println((allservices ? "getAllServiceReferences(" : "getServiceReferences(") + clazz + ", \"" + filterstring + "\")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
		//}
		/* Filter filter = (filterstring == null) ? null : context.createFilter(filterstring);
		List references = changeRegistrationsToReferences(lookupServiceRegistrations(clazz, filter));
		for (Iterator iter = references.iterator(); iter.hasNext();) {
			ServiceReferenceImpl reference = (ServiceReferenceImpl) iter.next();
			if (allservices || isAssignableTo(context, reference)) {
				try { // test for permission to get the service 
					checkGetServicePermission(reference);
				} catch (se:Error) {
					iter.remove();
				}
			} else {
				iter.remove();
			}
		}

		final Collection shrinkable = new ShrinkableCollection(references);
		notifyFindHooks(context, clazz, filterstring, allservices, shrinkable);

		int size = references.size();
		if (size == 0) {
			return null;
		}
		return (ServiceReferenceImpl[]) references.toArray(new ServiceReferenceImpl[size]); */
		return null;
	}

	/**
	 * Returns a <code>ServiceReference</code> object for a service that
	 * implements and was registered under the specified class.
	 * 
	 * <p>
	 * This <code>ServiceReference</code> object is valid at the time of the
	 * call to this method, however as the Framework is a very dynamic
	 * environment, services can be modified or unregistered at anytime.
	 * 
	 * <p>
	 * This method is the same as calling
	 * {@link BundleContext#getServiceReferences(String, String)} with a
	 * <code>null</code> filter string. It is provided as a convenience for
	 * when the caller is interested in any service that implements the
	 * specified class.
	 * <p>
	 * If multiple such services exist, the service with the highest ranking (as
	 * specified in its {@link Constants#SERVICE_RANKING} property) is returned.
	 * <p>
	 * If there is a tie in ranking, the service with the lowest service ID (as
	 * specified in its {@link Constants#SERVICE_ID} property); that is, the
	 * service that was registered first is returned.
	 * 
	 * @param context The BundleContext of the requesting bundle.
	 * @param clazz The class name with which the service was registered.
	 * @return A <code>ServiceReference</code> object, or <code>null</code>
	 *         if no services are registered which implement the named class.
	 * @throws java.lang.IllegalStateException If this BundleContext is no
	 *         longer valid.
	 */
	public function getServiceReference(context:BundleContext, clazz:String):ServiceReferenceImpl {
		try {
			var references:Array = getServiceReferences(context, clazz, null, false);

			if (references != null) {
				// Since we maintain the registrations in a sorted List, the first element is always the
				// correct one to return.
				return references[0];
			}
		} catch (e:Error) {
			/* if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
				Debug.println("InvalidSyntaxException w/ null filter" + e.getMessage()); //$NON-NLS-1$
				Debug.printStackTrace(e);
			} */
		}

		return null;
	}

	/**
	 * Returns the specified service object for a service.
	 * <p>
	 * A bundle's use of a service is tracked by the bundle's use count of that
	 * service. Each time a service's service object is returned by
	 * {@link #getService(ServiceReference)} the context bundle's use count for
	 * that service is incremented by one. Each time the service is released by
	 * {@link #ungetService(ServiceReference)} the context bundle's use count
	 * for that service is decremented by one.
	 * <p>
	 * When a bundle's use count for a service drops to zero, the bundle should
	 * no longer use that service.
	 * 
	 * <p>
	 * This method will always return <code>null</code> when the service
	 * associated with this <code>reference</code> has been unregistered.
	 * 
	 * <p>
	 * The following steps are required to get the service object:
	 * <ol>
	 * <li>If the service has been unregistered, <code>null</code> is
	 * returned.
	 * <li>The context bundle's use count for this service is incremented by
	 * one.
	 * <li>If the context bundle's use count for the service is currently one
	 * and the service was registered with an object implementing the
	 * <code>ServiceFactory</code> interface, the
	 * {@link ServiceFactory#getService(Bundle, ServiceRegistration)} method is
	 * called to create a service object for the context bundle. This service
	 * object is cached by the Framework. While the context bundle's use count
	 * for the service is greater than zero, subsequent calls to get the
	 * services's service object for the context bundle will return the cached
	 * service object. <br>
	 * If the service object returned by the <code>ServiceFactory</code>
	 * object is not an <code>instanceof</code> all the classes named when the
	 * service was registered or the <code>ServiceFactory</code> object throws
	 * an exception, <code>null</code> is returned and a Framework event of
	 * type {@link FrameworkEvent#ERROR} containing a {@link ServiceException}
	 * describing the error is fired.
	 * <li>The service object for the service is returned.
	 * </ol>
	 * 
	 * @param context The BundleContext of the requesting bundle.
	 * @param reference A reference to the service.
	 * @return A service object for the service associated with
	 *         <code>reference</code> or <code>null</code> if the service is
	 *         not registered, the service object returned by a
	 *         <code>ServiceFactory</code> does not implement the classes
	 *         under which it was registered or the <code>ServiceFactory</code>
	 *         threw an exception.
	 * @throws java.lang.SecurityException If the caller does not have the
	 *         <code>ServicePermission</code> to get the service using at
	 *         least one of the named classes the service was registered under
	 *         and the Java Runtime Environment supports permissions.
	 * @throws java.lang.IllegalStateException If this BundleContext is no
	 *         longer valid.
	 * @see #ungetService(ServiceReference)
	 * @see ServiceFactory
	 */
	public function getService(context:BundleContext, reference:ServiceReferenceImpl):Object {
		/* test for permission to get the service */
		//checkGetServicePermission(reference);

		return reference.getRegistration().getService(context);
	}

	/**
	 * Releases the service object referenced by the specified
	 * <code>ServiceReference</code> object. If the context bundle's use count
	 * for the service is zero, this method returns <code>false</code>.
	 * Otherwise, the context bundle's use count for the service is decremented
	 * by one.
	 * 
	 * <p>
	 * The service's service object should no longer be used and all references
	 * to it should be destroyed when a bundle's use count for the service drops
	 * to zero.
	 * 
	 * <p>
	 * The following steps are required to unget the service object:
	 * <ol>
	 * <li>If the context bundle's use count for the service is zero or the
	 * service has been unregistered, <code>false</code> is returned.
	 * <li>The context bundle's use count for this service is decremented by
	 * one.
	 * <li>If the context bundle's use count for the service is currently zero
	 * and the service was registered with a <code>ServiceFactory</code>
	 * object, the
	 * {@link ServiceFactory#ungetService(Bundle, ServiceRegistration, Object)}
	 * method is called to release the service object for the context bundle.
	 * <li><code>true</code> is returned.
	 * </ol>
	 * 
	 * @param context The BundleContext of the requesting bundle.
	 * @param reference A reference to the service to be released.
	 * @return <code>false</code> if the context bundle's use count for the
	 *         service is zero or if the service has been unregistered;
	 *         <code>true</code> otherwise.
	 * @throws java.lang.IllegalStateException If this BundleContext is no
	 *         longer valid.
	 * @see #getService
	 * @see ServiceFactory
	 */
	public function ungetService(context:BundleContext, reference:ServiceReferenceImpl):Boolean {
		var registration:ServiceRegistrationImpl = reference.getRegistration();

		return registration.ungetService(context);
	}

	/**
	 * Returns this bundle's <code>ServiceReference</code> list for all
	 * services it has registered or <code>null</code> if this bundle has no
	 * registered services.
	 * 
	 * <p>
	 * If the Java runtime supports permissions, a <code>ServiceReference</code>
	 * object to a service is included in the returned list only if the caller
	 * has the <code>ServicePermission</code> to get the service using at
	 * least one of the named classes the service was registered under.
	 * 
	 * <p>
	 * The list is valid at the time of the call to this method, however, as the
	 * Framework is a very dynamic environment, services can be modified or
	 * unregistered at anytime.
	 * 
	 * @param context The BundleContext of the requesting bundle.
	 * @return An array of <code>ServiceReference</code> objects or
	 *         <code>null</code>.
	 * @throws java.lang.IllegalStateException If this bundle has been
	 *         uninstalled.
	 * @see ServiceRegistration
	 * @see ServiceReference
	 * @see ServicePermission
	 */
	public function getRegisteredServices(context:BundleContext):Array {
		var references:Array = changeRegistrationsToReferences(lookupServiceRegistrations2(context));
		/* for (Iterator iter = references.iterator(); iter.hasNext();) {
			ServiceReferenceImpl reference = (ServiceReferenceImpl) iter.next();
			try { // test for permission to get the service 
				checkGetServicePermission(reference);
			} catch (SecurityException se) {
				iter.remove();
			}
		}
		if (references.length == 0) {
			return null;
		} */
		return references;
	}

	/**
	 * Returns this bundle's <code>ServiceReference</code> list for all
	 * services it is using or returns <code>null</code> if this bundle is not
	 * using any services. A bundle is considered to be using a service if its
	 * use count for that service is greater than zero.
	 * 
	 * <p>
	 * If the Java Runtime Environment supports permissions, a
	 * <code>ServiceReference</code> object to a service is included in the
	 * returned list only if the caller has the <code>ServicePermission</code>
	 * to get the service using at least one of the named classes the service
	 * was registered under.
	 * <p>
	 * The list is valid at the time of the call to this method, however, as the
	 * Framework is a very dynamic environment, services can be modified or
	 * unregistered at anytime.
	 * 
	 * @param context The BundleContext of the requesting bundle.
	 * @return An array of <code>ServiceReference</code> objects or
	 *         <code>null</code>.
	 * @throws java.lang.IllegalStateException If this bundle has been
	 *         uninstalled.
	 * @see ServiceReference
	 * @see ServicePermission
	 */
	public function getServicesInUse(context:BundleContext):Array {
		/* Map servicesInUse = context.getServicesInUseMap();
		if (servicesInUse == null) {
			return null;
		}

		List references;
		synchronized (servicesInUse) {
			if (servicesInUse.size() == 0) {
				return null;
			}
			references = changeRegistrationsToReferences(new ArrayList(servicesInUse.keySet()));
		}
		for (Iterator iter = references.iterator(); iter.hasNext();) {
			ServiceReferenceImpl reference = (ServiceReferenceImpl) iter.next();
			try { // test for permission to get the service 
				checkGetServicePermission(reference);
			} catch (SecurityException se) {
				iter.remove();
			}
		}

		int size = references.size();
		if (size == 0) {
			return null;
		}
		return (ServiceReferenceImpl[]) references.toArray(new ServiceReferenceImpl[size]); */
		return null;
	}

	/**
	 * Called when the BundleContext is closing to unregister all services
	 * currently registered by the bundle.
	 * 
	 * @param context The BundleContext of the closing bundle.
	 */
	public function unregisterServices(context:BundleContext):void {
		/* List registrations = lookupServiceRegistrations(context);
		for (Iterator iter = registrations.iterator(); iter.hasNext();) {
			ServiceRegistrationImpl registration = (ServiceRegistrationImpl) iter.next();
			try {
				registration.unregister();
			} catch (e:Error) {
				// already unregistered 
			}
		} */
	}

	/**
	 * Called when the BundleContext is closing to unget all services
	 * currently used by the bundle.
	 * 
	 * @param context The BundleContext of the closing bundle.
	 */
	public function releaseServicesInUse(context:BundleContext):void {
		/* Map servicesInUse = context.getServicesInUseMap();
		if (servicesInUse == null) {
			return;
		}
		List registrations;
		synchronized (servicesInUse) {
			if (servicesInUse.size() == 0) {
				return;
			}
			registrations = new ArrayList(servicesInUse.keySet());
		}
		if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
			Debug.println("Releasing services"); //$NON-NLS-1$
		}
		for (Iterator iter = registrations.iterator(); iter.hasNext();) {
			ServiceRegistrationImpl registration = (ServiceRegistrationImpl) iter.next();
			registration.releaseService(context);
		} */
	}

	/**
	 * Add a new Service Listener for a bundle.
	 * 
	 * @param context Context of bundle adding listener.
	 * @param listener Service Listener to be added.
	 * @param filter Filter string for listener or null.
	 * @throws InvalidSyntaxException If the filter string is invalid.
	 */
	public function addServiceListener(context:BundleContext, listener:Function, filter:String):void {
		/* if (Debug.DEBUG && Debug.DEBUG_EVENTS) {
			String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
			Debug.println("addServiceListener[" + context.getBundleImpl() + "](" + listenerName + ", \"" + filter + "\")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
		}

		FilteredServiceListener filteredListener = new FilteredServiceListener(context, listener, filter);
		FilteredServiceListener oldFilteredListener;
		synchronized (serviceEventListeners) {
			Map listeners = (Map) serviceEventListeners.get(context);
			if (listeners == null) {
				listeners = new CopyOnWriteIdentityMap();
				serviceEventListeners.put(context, listeners);
			}
			oldFilteredListener = (FilteredServiceListener) listeners.put(listener, filteredListener);
		}

		if (oldFilteredListener != null) {
			oldFilteredListener.markRemoved();
			Collection removedListeners = Collections.singletonList(oldFilteredListener);
			notifyListenerHooks(removedListeners, false);
		}

		Collection addedListeners = Collections.singletonList(filteredListener);
		notifyListenerHooks(addedListeners, true); */
	}

	/**
	 * Remove a Service Listener for a bundle.
	 * 
	 * @param context Context of bundle removing listener.
	 * @param listener Service Listener to be removed.
	 */
	public function removeServiceListener(context:BundleContext, listener:Function):void {
/* 		FilteredServiceListener oldFilteredListener;
		synchronized (serviceEventListeners) {
			Map listeners = (Map) serviceEventListeners.get(context);
			if (listeners == null) {
				return; // this context has no listeners to begin with
			}
			oldFilteredListener = (FilteredServiceListener) listeners.remove(listener);
		}

		if (oldFilteredListener == null) {
			return;
		}
		oldFilteredListener.markRemoved();
		Collection removedListeners = Collections.singletonList(oldFilteredListener);
		notifyListenerHooks(removedListeners, false); */
	}

	/**
	 * Remove all Service Listener for a bundle.
	 * 
	 * @param context Context of bundle removing all listeners.
	 */
	public function removeAllServiceListeners(context:BundleContext):void {
		/* Map removedListenersMap;
		synchronized (serviceEventListeners) {
			removedListenersMap = (Map) serviceEventListeners.remove(context);
		}
		if ((removedListenersMap == null) || (removedListenersMap.size() == 0)) {
			return;
		}
		Collection removedListeners = removedListenersMap.values();
		for (Iterator iter = removedListeners.iterator(); iter.hasNext();) {
			FilteredServiceListener oldFilteredListener = (FilteredServiceListener) iter.next();
			oldFilteredListener.markRemoved();
		}
		notifyListenerHooks(removedListeners, false); */
	}

	/**
	 * Deliver a ServiceEvent.
	 * 
	 * @param event The ServiceEvent to deliver.
	 */
	public function publishServiceEvent(event:ServiceEvent):void {
		/* if (System.getSecurityManager() == null) {
			publishServiceEventPrivileged(event);
		} else {
			AccessController.doPrivileged(new PrivilegedAction() {
				public Object run() {
					publishServiceEventPrivileged(event);
					return null;
				}
			});
		} */
	}

	public function publishServiceEventPrivileged(event:ServiceEvent):void {
		/* Build the listener snapshot */
		//Map /*<BundleContext,Set<Map.Entry<Object,Object>>>*/listenerSnapshot;
		/* synchronized (serviceEventListeners) {
			listenerSnapshot = new HashMap(serviceEventListeners.size());
			for (Iterator iter = serviceEventListeners.entrySet().iterator(); iter.hasNext();) {
				Map.Entry entry = (Map.Entry) iter.next();
				BundleContext context = (BundleContext) entry.getKey();
				Map listeners = (Map) entry.getValue();
				if (!listeners.isEmpty()) {
					listenerSnapshot.put(context, listeners.entrySet());
				}
			}
		} */

		/* shrink the snapshot.
		 * keySet returns a Collection which cannot be added to and
		 * removals from that collection will result in removals of the
		 * entry from the snapshot.
		 */
		//Collection/*<BundleContext>*/shrinkable = listenerSnapshot.keySet();
		/* notifyEventHooksPrivileged(event, shrinkable);
		if (listenerSnapshot.isEmpty()) {
			return;
		} */

		/* deliver the event to the snapshot */
		/* ListenerQueue queue = framework.newListenerQueue();
		for (Iterator iter = listenerSnapshot.entrySet().iterator(); iter.hasNext();) {
			Map.Entry entry = (Map.Entry) iter.next();
			EventDispatcher dispatcher = (BundleContext) entry.getKey();
			Set listeners = (Set) entry.getValue();
			queue.queueListeners(listeners, dispatcher);
		}
		queue.dispatchEventSynchronous(SERVICEEVENT, event); */
	}

	/**
	 * Return the next available service id.
	 * 
	 * @return next service id.
	 */
	public function getNextServiceId():Number {
		var id:Number = serviceid;
		serviceid++;
		return id;
	}

	/**
	 * Add the ServiceRegistrationImpl to the data structure.
	 * Logic:
	 *    
	 * @param context The BundleContext of the bundle registering the service.
	 * @param registration The new ServiceRegistration.
	 */
	/* @GuardedBy("this") */
	public function addServiceRegistration(context:BundleContext, registration:ServiceRegistrationImpl):void {
		// Add the ServiceRegistrationImpl to the list of Services published by BundleContext.
		//var contextServices:Array = publishedServicesByContext[context] as Array;
		//if (contextServices == null) {
		//	contextServices = new Array();
		//	publishedServicesByContext[context]= contextServices;
		//}
		// The list is NOT sorted, so we just add
		//contextServices.push(registration);

		// Add the ServiceRegistrationImpl to the list of Services published by Class Name.
		var clazzes:Array = registration.getClasses();
		var insertIndex:int;
		for each(var clazz:String in clazzes) {
			var services:Array = publishedServicesByClass[clazz] as Array;
			if (services == null) {
				services = new Array();
				publishedServicesByClass[clazz] = services;
			}
			// The list is sorted, so we must find the proper location to insert
			//insertIndex = -Collections.binarySearch(services, registration) - 1;
			//services.add(insertIndex, registration);
			services.push(registration);
		}

		// Add the ServiceRegistrationImpl to the list of all published Services.
		// The list is sorted, so we must find the proper location to insert
		//insertIndex = -Collections.binarySearch(allPublishedServices, registration) - 1;
		//allPublishedServices.add(insertIndex, registration);
		allPublishedServices.push(registration);
	}

	/**
	 * Remove the ServiceRegistrationImpl from the data structure.
	 * 
	 * @param context The BundleContext of the bundle registering the service.
	 * @param registration The ServiceRegistration to remove.
	 */
	/* @GuardedBy("this") */
	public function removeServiceRegistration(context:BundleContext, serviceReg:ServiceRegistrationImpl):void {
		// Remove the ServiceRegistrationImpl from the list of Services published by BundleContext.
		var contextServices:Array = publishedServicesByContext[context] as Array;
		if (contextServices != null) {
		    var list:ArrayCollection = new ArrayCollection(contextServices);
		    for (var index:int=0;index<list.length;index++) {
		        var item:ServiceRegistration = list.getItemAt(index) as ServiceRegistration;
		        if(item == serviceReg) {
		            list.removeItemAt(index);
		            break;
		        }
		    }
		}

		// Remove the ServiceRegistrationImpl from the list of Services published by Class Name.
		var clazzes:Array = serviceReg.getClasses();
		for (var i:int = 0, size:int = clazzes.length; i < size; i++) {
			var clazz:String = clazzes[i];
			var services:Array = publishedServicesByClass[clazz] as Array;
			var list1:ArrayCollection = new ArrayCollection(services);
			for (var j:int=0;j<list1.length;j++) {
		        var item1:ServiceRegistration = list1.getItemAt(j) as ServiceRegistration;
		        if(item1 == serviceReg) {
		            list1.removeItemAt(j);
		            break;
		        }
		    }
		}

		// Remove the ServiceRegistrationImpl from the list of all published Services.
		// allPublishedServices.remove(serviceReg);
		var list2:ArrayCollection = new ArrayCollection(allPublishedServices);
		for (var k:int=0;k<list2.length;k++) {
	        var item2:ServiceRegistration = list2.getItemAt(k) as ServiceRegistration;
	        if(item2 == serviceReg) {
	            list2.removeItemAt(k);
	            break;
	        }
	    }
	}

	/**
	 * Lookup Service Registrations in the data structure by class name and filter.
	 * 
	 * @param clazz The class name with which the service was registered or
	 *        <code>null</code> for all services.
	 * @param filter The filter criteria.
	 * @return List<ServiceRegistrationImpl>
	 */
	private function lookupServiceRegistrations(clazz:String, filter:Function):Array {
		var services:Array;
		//synchronized (this) {
			if (clazz == null) { /* all services */
				services = allPublishedServices;
			} else {
				/* services registered under the class name */
				services = publishedServicesByClass[clazz] as Array;
			}

			if ((services == null) || (services.length == 0)) {
				return new Array();
			}
		//}
        
		if (filter == null) {
			return services;
		}
        var result:Array = new Array();
		for each(var item:ServiceRegistrationImpl in services) {
			if (filter.apply(this, [item]) == true) {
				result.push(item);
			}
		}
		return result;
	}

	/**
	 * Lookup Service Registrations in the data structure by BundleContext.
	 * 
	 * @param context The BundleContext for which to return Service Registrations.
	 * @return List<ServiceRegistrationImpl>
	 */
	private function lookupServiceRegistrations2(context:BundleContext):Array {
		var result:Array = publishedServicesByContext[context] as Array;

		if ((result == null) || (result.length == 0)) {
			return new Array();
		}

		return new Array(result); /* make a new list since we don't want to change the real list */
	}

	/**
	 * Modify a List<ServiceRegistrationImpl> in place to a List<ServiceReferenceImpl>.
	 * 
	 * @param result The input List<ServiceRegistrationImpl>.
	 * @return result which has been changed to List<ServiceReferenceImpl>
	 */
	private static function changeRegistrationsToReferences(result:Array):Array {
		/* for (ListIterator iter = result.listIterator(); iter.hasNext();) {
			ServiceRegistrationImpl registration = (ServiceRegistrationImpl) iter.next();
			iter.set(registration.getReferenceImpl()); 
		} */
		return new Array(); // result;
	}

	/**
	 * Check for permission to register a service.
	 * 
	 * The caller must have permission for ALL names.
	 */
	private static function checkRegisterServicePermission(names:Array):void {
		/* SecurityManager sm = System.getSecurityManager();
		if (sm == null) {
			return;
		}
		for (int i = 0, len = names.length; i < len; i++) {
			sm.checkPermission(new ServicePermission(names[i], ServicePermission.REGISTER));
		} */
	}

	/** 
	 * Return the name of the class that is not satisfied by the service object. 
	 * @param clazzes Array of class names.
	 * @param serviceObject Service object.
	 * @return The name of the class that is not satisfied by the service object.
	 */
	protected static function checkServiceClass(clazzes:Array, serviceObject:Object):String {
		/* ClassLoader cl = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
			public Object run() {
				return serviceObject.getClass().getClassLoader();
			}
		});
		for (int i = 0, len = clazzes.length; i < len; i++) {
			try {
				Class serviceClazz = cl == null ? Class.forName(clazzes[i]) : cl.loadClass(clazzes[i]);
				if (!serviceClazz.isInstance(serviceObject))
					return clazzes[i];
			} catch (ClassNotFoundException e) {
				//This check is rarely done
				if (extensiveCheckServiceClass(clazzes[i], serviceObject.getClass()))
					return clazzes[i];
			}
		} */
		return null;
	}

	private static function extensiveCheckServiceClass(clazz:String, serviceClazz:Class):Boolean {
		try {
		    var clas:Class = getDefinitionByName(clazz) as Class;
		    if (serviceClazz is clas) {
		        return true;
		    }
		} catch(er:Error) {
		    trace(er.getStackTrace());
		}
		return false;
	}
}
}